Imports iSoft.Common.Common.Variants

Namespace Common

    Public Class Methods

        Private Const ERADATE_LOCAL As String = "en-US"
        Private Const HASHALGORITHM As String = "SHA1"

        ''' <summary>
        ''' Random string type.
        ''' </summary>
        ''' <remarks></remarks>
        Public Enum RndStringKind
            Normal = 0
            Numeric = 1
            Letter = 2
        End Enum

        ''' <summary>
        ''' Get the file name (with extend name or not).
        ''' </summary>
        ''' <param name="FullName">The file name with full path.</param>
        ''' <param name="WithExtendName">Return with extend name or not.</param>
        ''' <param name="SegmentChar">Segment char (Default: \).</param>
        ''' <returns>File name (with extend name).</returns>
        ''' <remarks></remarks>
        Public Shared Function GetFileName(ByVal FullName As String, ByVal WithExtendName As Boolean, Optional ByVal SegmentChar As Char = ENMARK) As String
            Dim ret, FileName As String
            Dim intPos As Integer = FullName.LastIndexOf(SegmentChar)
            If intPos >= 0 Then
                FileName = FullName.Substring(intPos + 1, FullName.Length - intPos - 1)
            Else
                FileName = FullName
            End If
            If WithExtendName = True Then
                ret = FileName
            Else
                intPos = FileName.LastIndexOf(DOT)
                If intPos >= 0 Then
                    ret = FileName.Substring(0, intPos)
                Else
                    ret = FileName
                End If
            End If
            Return ret
        End Function

        ''' <summary>
        ''' Get the file path.
        ''' </summary>
        ''' <param name="FullName">The file name with full path.</param>
        ''' <returns>File path.</returns>
        ''' <param name="SegmentChar">Segment char (Default: \).</param>
        ''' <remarks></remarks>
        Public Shared Function GetFilePath(ByVal FullName As String, Optional ByVal SegmentChar As Char = ENMARK) As String
            Dim ret As String
            Dim intPos As Integer = FullName.LastIndexOf(SegmentChar)
            If intPos >= 0 Then
                ret = FullName.Substring(0, intPos)
            Else
                ret = ""
            End If
            Return ret
        End Function

        ''' <summary>
        ''' Get the file extend name (null if no extend name).
        ''' </summary>
        ''' <param name="FullName">The file name with full path.</param>
        ''' <returns>File extend name.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetExtendName(ByVal FullName As String) As String
            Dim ret As String
            Dim intPos As Integer
            If FullName.EndsWith(DOT) = False Then
                intPos = FullName.LastIndexOf(DOT)
                If intPos >= 0 Then
                    ret = FullName.Substring(intPos + 1)
                Else
                    ret = ""
                End If
            Else
                ret = ""
            End If
            Return ret
        End Function

        ''' <summary>
        ''' Get the GUID attribute from a type.
        ''' </summary>
        ''' <param name="obj">The type object witch have a GUID attribute.</param>
        ''' <param name="WithBracket">Returning GUID string with the bracket if set to TRUE.</param>
        ''' <returns>The GUID string of the type.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetGUID(ByVal obj As System.Type, ByVal WithBracket As Boolean) As String
            Dim ret As String = ""
            Try
                Dim attrib As System.Attribute = System.Attribute.GetCustomAttribute(obj, GetType(System.Runtime.InteropServices.GuidAttribute))
                Dim guid As New System.Guid(CType(attrib, System.Runtime.InteropServices.GuidAttribute).Value)
                ret = guid.ToString.ToUpper
                If ret.Length > 0 AndAlso ret <> "" AndAlso WithBracket = True Then
                    ret = BRACKET_BIG_LEFT & ret & BRACKET_BIG_RIGHT
                End If
            Catch ex As Exception
                ret = ""
            End Try
            Return ret
        End Function

        ''' <summary>
        ''' Get the ProgId attribute from a type.
        ''' </summary>
        ''' <param name="obj">The type object witch have a GUID attribute.</param>
        ''' <returns>The ProgId string of the type.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetProgId(ByVal obj As System.Type) As String
            Dim ret As String
            Try
                Dim attrib As System.Attribute = System.Attribute.GetCustomAttribute(obj, GetType(System.Runtime.InteropServices.ProgIdAttribute))
                ret = CType(attrib, System.Runtime.InteropServices.ProgIdAttribute).Value
            Catch ex As Exception
                ret = ""
            End Try
            Return ret
        End Function

        ''' <summary>
        ''' Begin a DOS command with the parameters and return the output string.
        ''' </summary>
        ''' <param name="FileName">The DOS command file full name.</param>
        ''' <param name="Parameter">The parameters of the command.</param>
        ''' <param name="Result">The output string in the console.</param>
        ''' <returns>The command successfully operated.</returns>
        ''' <remarks></remarks>
        Public Shared Function Command(ByVal FileName As String, ByVal Parameter As String, Optional ByVal NeedResult As Boolean = False, Optional ByRef Result As String = "") As Boolean
            Dim ret As Boolean = False
            Dim p As Process
            Try
                p = New Process
                p.StartInfo.RedirectStandardOutput = True
                p.StartInfo.CreateNoWindow = True
                p.StartInfo.FileName = FileName
                p.StartInfo.Arguments = Parameter
                p.StartInfo.UseShellExecute = False
                p.Start()
                If NeedResult = True Then
                    Result = p.StandardOutput.ReadToEnd
                End If
                ret = True
            Catch ex As Exception
                ret = False
            End Try
            Return ret
        End Function

        ''' <summary>
        ''' The KMP search.
        ''' </summary>
        ''' <typeparam name="T">The type of the array searching and searched.</typeparam>
        ''' <param name="src">The searching array.</param>
        ''' <param name="key">The array to search.</param>
        ''' <returns>The first match position in src array.</returns>
        ''' <remarks></remarks>
        Public Shared Function KMPSearch(Of T As IComparable(Of T))(ByVal src() As T, ByVal key() As T) As Integer
            Dim ret As Integer = 0
            Dim arrNext() As Integer
            Dim inti As Integer = 1
            Dim intj As Integer = 1
            If src Is Nothing OrElse key Is Nothing OrElse key.Length > src.Length Then
                ret = -1
            Else
                arrNext = KMPSearchGetNextArray(Of T)(key)
                While inti <= src.Length AndAlso intj <= key.Length
                    If intj = 0 OrElse src(inti - 1).Equals(key(intj - 1)) = True Then
                        inti = inti + 1
                        intj = intj + 1
                    Else
                        intj = arrNext(intj)
                    End If
                End While
                If intj > key.Length Then
                    ret = inti - key.Length - 1
                Else
                    ret = -1
                End If
            End If
            Return ret
        End Function

        ''' <summary>
        ''' Get the next array function in KMP search.
        ''' </summary>
        ''' <typeparam name="T">The type of the array searching and searched.</typeparam>
        ''' <param name="key">The array to search.</param>
        ''' <returns>The value of each item of the array to search.</returns>
        ''' <remarks>This function is Private and is used by KMPSearch.</remarks>
        Private Shared Function KMPSearchGetNextArray(Of T As IComparable(Of T))(ByVal key() As T) As Integer()
            Dim arrNext(key.Length) As Integer
            Dim intj As Integer = 1
            Dim intk As Integer = 0
            arrNext(1) = 0
            While intj < key.Length
                If intk = 0 OrElse key(intj - 1).Equals(key(intk - 1)) = True Then
                    intj = intj + 1
                    intk = intk + 1
                    arrNext(intj) = intk
                Else
                    intk = arrNext(intk)
                End If
            End While
            Return arrNext
        End Function

        ''' <summary>
        ''' Compute the expression string and return the result by CodeDOM.
        ''' </summary>
        ''' <param name="Expression">The expression string.</param>
        ''' <returns>The compute result.</returns>
        ''' <remarks></remarks>
        Public Shared Function Eval(ByVal Expression As String) As Object
            Dim ret As Object = Nothing
            Dim CodeBuilder As New Text.StringBuilder
            Dim comp As CodeDom.Compiler.CodeDomProvider = New Microsoft.VisualBasic.VBCodeProvider
            Dim cp As New CodeDom.Compiler.CompilerParameters
            Dim mi As Reflection.MethodInfo
            CodeBuilder.Append("Imports System" & vbCrLf)
            CodeBuilder.Append("Imports System.Math" & vbCrLf)
            CodeBuilder.Append("Imports Microsoft.VisualBasic" & vbCrLf)
            CodeBuilder.Append(vbCrLf)
            CodeBuilder.Append("Public Module Mode" & vbCrLf)
            CodeBuilder.Append("   Public Function Func() As Object" & vbCrLf)
            CodeBuilder.Append("        Return " & Expression & vbCrLf)
            CodeBuilder.Append("   End Function" & vbCrLf)
            CodeBuilder.Append("End Module" & vbCrLf)
            cp.ReferencedAssemblies.Add("System.dll")
            cp.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")
            cp.GenerateExecutable = False
            cp.GenerateInMemory = True
            Dim code As String = CodeBuilder.ToString
            Dim cr As CodeDom.Compiler.CompilerResults = comp.CompileAssemblyFromSource(cp, code)
            Dim asm As Reflection.Assembly
            Dim t As Type
            If cr.Errors.HasErrors = False Then
                asm = cr.CompiledAssembly
                t = asm.GetType("Mode")
                mi = t.GetMethod("Func", Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public)
                ret = mi.Invoke(Nothing, New Object() {})
            End If
            Return ret
        End Function

        ''' <summary>
        ''' Suffle the array in defualt 7 times.
        ''' </summary>
        ''' <typeparam name="T">The type of the array.</typeparam>
        ''' <param name="List">The array to suffle.</param>
        ''' <param name="Times">The suffle times.</param>
        ''' <remarks></remarks>
        Public Shared Sub Shuffle(Of T)(ByRef List() As T, Optional ByVal Times As Integer = 7)
            Dim i, ts As Long
            Dim pos As Long
            Dim tmp As Object
            Dim seed As Single
            'Currect the mismatch value of the Times
            If Times <= 0 Then Times = 7
            'Loop the times to shuffle
            For ts = 1 To Times
                'Once shuffle operator
                seed = Now.TimeOfDay.Milliseconds
                For i = List.Length - 1 To 0 Step -1
                    'Radom the position to changed
                    pos = Rnd(seed) * (i + 1)
                    'Changed the item
                    tmp = List(pos)
                    List(pos) = List(i)
                    List(i) = tmp
                Next
            Next
        End Sub

        ''' <summary>
        ''' Convert the EXCEL Data to VB.NET Data type object.
        ''' </summary>
        ''' <param name="sDate">The date in EXCEL type.</param>
        ''' <returns>The VB type Date.</returns>
        ''' <remarks></remarks>
        Public Shared Function ConvertExcelDateToDate(ByVal sDate As String) As DateTime
            Dim december31st1899 As New DateTime(1899, 12, 31)
            Dim march1st1900 As New DateTime(1900, 3, 1)
            Dim after1stMarchAdjustment As New TimeSpan(1, 0, 0, 0)
            Dim ts As TimeSpan = TimeSpan.Parse(sDate)
            Dim dt As DateTime = december31st1899.Add(ts)
            If dt >= march1st1900 Then
                Return dt.AddDays(-1)
            End If
            Return dt
        End Function

        ''' <summary>
        ''' Return a string in random munber and letter.
        ''' </summary>
        ''' <param name="Style">The style of the random string.</param>
        ''' <param name="Length">The length of the random string.</param>
        ''' <returns>The random string.</returns>
        ''' <remarks></remarks>
        Public Shared Function RndString(ByVal Style As RndStringKind, ByVal Length As Integer) As String
            Dim result As New System.Text.StringBuilder
            Dim tmpNum As Integer
            For i As Integer = 0 To Length - 1
                tmpNum = 0
                Select Case Style
                    Case RndStringKind.Normal
                        While Not ((tmpNum > 47 And tmpNum < 58) Or (tmpNum > 64 And tmpNum < 91) Or (tmpNum > 96 And tmpNum < 122))
                            Randomize()
                            tmpNum = CInt(Rnd() * 128)
                        End While
                    Case RndStringKind.Letter
                        While Not ((tmpNum > 64 And tmpNum < 91) Or (tmpNum > 96 And tmpNum < 122))
                            Randomize()
                            tmpNum = CInt(Rnd() * 128)
                        End While
                    Case RndStringKind.Numeric
                        While Not ((tmpNum > 47 And tmpNum < 58))
                            Randomize()
                            tmpNum = CInt(Rnd() * 128)
                        End While
                End Select
                result.Append(Chr(tmpNum))
            Next
            Return result.ToString
        End Function

        ''' <summary>
        ''' Get current system encoding.
        ''' </summary>
        ''' <returns>Current system encoding.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetCurrentEncoding() As System.Text.Encoding
            Return System.Text.Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage)
        End Function

        ''' <summary>
        ''' Get the byte count in current codepage of the string.
        ''' </summary>
        ''' <param name="src">The string to calculate.</param>
        ''' <returns>The byte count.</returns>
        ''' <remarks></remarks>
        Public Shared Function LenB(ByVal src As String) As Integer
            Return GetCurrentEncoding.GetByteCount(src)
        End Function

        ''' <summary>
        ''' Get the substring by index of byte and count of byte.
        ''' </summary>
        ''' <param name="src">The string to subtract.</param>
        ''' <param name="start">Start byte position.</param>
        ''' <param name="count">Subtract byte count.</param>
        ''' <returns>Substring.</returns>
        ''' <remarks></remarks>
        Public Shared Function MidB(ByVal src As String, ByVal start As Integer, ByVal count As Integer) As String
            Dim ret As String = String.Empty
            Dim i, last As Integer
            Dim srcChs() As Byte = GetCurrentEncoding.GetBytes(src)
            Dim destChs As New Generic.List(Of Byte)
            last = Math.Min(start + count - 1, srcChs.Length - 1)
            For i = start To last
                destChs.Add(srcChs(i))
            Next
            ret = GetCurrentEncoding.GetString(destChs.ToArray)
            Return ret
        End Function

        ''' <summary>
        ''' Pad the string with binary counter.
        ''' </summary>
        ''' <param name="src">Source string.</param>
        ''' <param name="Length">Length to pad.</param>
        ''' <param name="PaddingChar">Padding char to fill the blank.</param>
        ''' <returns>Padded string.</returns>
        ''' <remarks></remarks>
        Public Shared Function PadRightB(ByVal src As String, ByVal Length As Integer, ByVal PaddingChar As Char) As String
            Dim sb As New System.Text.StringBuilder(src, Length)
            Dim len As Integer = LenB(src)
            For i As Integer = len To Length - 1
                sb.Append(PaddingChar)
            Next
            Return sb.ToString
        End Function

        ''' <summary>
        ''' RSAPKCS #1 1.5 Digital signature verify.
        ''' </summary>
        ''' <param name="PublicKey">The public key of signature.</param>
        ''' <param name="Name">Verify user name.</param>
        ''' <param name="Signature">Verify signature.</param>
        ''' <returns>Verify result.</returns>
        ''' <remarks></remarks>
        Public Shared Function VerifySignature(ByVal PublicKey As String, ByVal Name As String, ByVal Signature As String) As Boolean
            Dim ret As Boolean = False
            Try
                Dim rsa As New Security.Cryptography.RSACryptoServiceProvider
                rsa.FromXmlString(PublicKey)
                Dim f As New Security.Cryptography.RSAPKCS1SignatureDeformatter(rsa)
                f.SetHashAlgorithm(HASHALGORITHM)
                Dim key() As Byte = Convert.FromBase64String(Signature)
                Dim sha As New Security.Cryptography.SHA1Managed
                Dim nm() As Byte = sha.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(Name))
                ret = f.VerifySignature(nm, key)
            Catch ex As Exception
                ret = False
            End Try
            Return ret
        End Function

        ''' <summary>
        ''' RSAPKCS #1 1.5 Digital signature verify with machine code.
        ''' </summary>
        ''' <param name="PublicKey">The public key of signature.</param>
        ''' <param name="MachineCode">Machine code. (Created by GetHardDiskSerialCode(True).)</param>
        ''' <param name="Name">Verify user name.</param>
        ''' <param name="Signature">Verify signature.</param>
        ''' <returns>Verify result.</returns>
        ''' <remarks></remarks>
        Public Shared Function VerifySignature(ByVal PublicKey As String, ByVal MachineCode As String, ByVal Name As String, ByVal Signature As String) As Boolean
            Dim ret As Boolean = False
            Try
                Dim rsa As New Security.Cryptography.RSACryptoServiceProvider
                rsa.FromXmlString(PublicKey)
                Dim f As New Security.Cryptography.RSAPKCS1SignatureDeformatter(rsa)
                f.SetHashAlgorithm(HASHALGORITHM)
                Dim key() As Byte = Convert.FromBase64String(Signature)
                Dim sha As New Security.Cryptography.SHA1Managed
                Dim mc As String = MachineCode.Replace(CUT, String.Empty)
                Dim mc_nm As String = mc.Substring(0, mc.Length / 2) & Name & mc.Substring(mc.Length / 2)
                Dim nm() As Byte = sha.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(mc_nm))
                ret = f.VerifySignature(nm, key)
            Catch ex As Exception
                ret = False
            End Try
            Return ret
        End Function

        ''' <summary>
        ''' Smart Trim string in middle of it and replace the trimed substring bu trim-comment.
        ''' </summary>
        ''' <param name="Text">String to trim.</param>
        ''' <param name="MaxLength">The max length of trimed string.</param>
        ''' <param name="TrimStartIndex">Start position to replace to the trim-comment.</param>
        ''' <param name="TrimString">Trim-comment to replace when trimed.</param>
        ''' <returns>Trimed string.</returns>
        ''' <remarks></remarks>
        Public Shared Function StringSmartTrim(ByVal Text As String, ByVal MaxLength As Integer, ByVal TrimStartIndex As Integer, Optional ByVal TrimString As String = "...") As String
            Dim ret As String = String.Empty
            If Text.Length <= MaxLength Then
                ret = Text
            ElseIf TrimStartIndex + TrimString.Length + 1 < MaxLength Then
                ret = Text.Substring(0, TrimStartIndex + 1) & TrimString & Text.Substring(Text.Length - (MaxLength - (TrimStartIndex + 1) - TrimString.Length))
            Else
                ret = Text.Substring(0, MaxLength)
            End If
            Return ret
        End Function

        Private Declare Ansi Function GetVolumeInformation Lib "kernel32.dll" Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Integer, ByRef lpVolumeSerialNumber As Integer, ByVal lpMaximumComponentLength As Integer, ByVal lpFileSystemFlags As Integer, ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As Integer) As Integer

        ''' <summary>
        ''' Get the first partition serial code.
        ''' </summary>
        ''' <param name="MD5">Return the code with MD5.</param>
        ''' <returns>Partition serial code</returns>
        ''' <remarks></remarks>
        Public Shared Function GetHardDiskSerialCode(ByVal MD5 As Boolean) As String
            Dim ret As String = String.Empty
            Dim MAX_FILENAME_LEN As Integer = 256
            Dim retVal As Integer = 0
            Dim a As Integer = 0
            Dim b As Integer = 0
            Dim str1 As String = String.Empty
            Dim str2 As String = String.Empty
            Dim i As Integer = GetVolumeInformation(My.Computer.FileSystem.Drives.Item(0).Name, str1, MAX_FILENAME_LEN, retVal, a, b, str2, MAX_FILENAME_LEN)
            ret = retVal.ToString("x")
            If MD5 = True Then
                Dim _md5 As Security.Cryptography.MD5 = Security.Cryptography.MD5.Create
                Dim data() As Byte = _md5.ComputeHash(System.Text.Encoding.Default.GetBytes(ret))
                Dim sb As New System.Text.StringBuilder
                For j As Integer = 0 To data.Length - 1
                    If j > 0 AndAlso j Mod 2 = 0 Then
                        sb.Append(CUT)
                    End If
                    sb.Append(data(j).ToString("x2"))
                Next
                ret = sb.ToString
            End If
            Return ret
        End Function

        ''' <summary>
        ''' Get the relative path.
        ''' </summary>
        ''' <param name="basePath">The base path.</param>
        ''' <param name="combinePath">The target combine path.</param>
        ''' <returns>The relative path.</returns>
        ''' <remarks></remarks>
        Public Shared Function RelativePath(ByVal basePath As String, ByVal combinePath As String, Optional ByVal Separater As String = Variants.ENMARK) As String
            Dim ret As String = combinePath
            Dim bPath() As String = basePath.Split(Separater)
            Dim cPath() As String = combinePath.Split(Separater)
            Dim i, j, parentCnt As Integer
            Dim sb As New System.Text.StringBuilder
            While i <= bPath.Length - 1 AndAlso String.Compare(bPath(i), cPath(i), True) = 0
                i = i + 1
            End While
            parentCnt = bPath.Length - i
            If parentCnt < bPath.Length Then
                For j = 1 To parentCnt
                    sb.Append("..")
                    sb.Append(Separater)
                Next
                For j = i To cPath.Length - 1
                    sb.Append(cPath(j))
                    If j < cPath.Length - 1 Then
                        sb.Append(Separater)
                    End If
                Next
                ret = sb.ToString
            End If
            Return ret
        End Function

        ''' <summary>
        ''' DES Encrypt string
        ''' </summary>
        ''' <param name="Text">String to encrypt</param>
        ''' <param name="Key">Encrypt key</param>
        ''' <param name="IV">Encrypt vector</param>
        ''' <returns>Encrypted string</returns>
        ''' <remarks></remarks>
        Public Shared Function DESEncrypt(ByVal Text As String, ByVal Key() As Byte, ByVal IV() As Byte) As String
            Dim des As New Security.Cryptography.DESCryptoServiceProvider
            Dim inpBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(Text)
            des.Key = Key
            des.IV = IV
            Dim ms As New IO.MemoryStream
            Dim cs As New Security.Cryptography.CryptoStream(ms, des.CreateEncryptor, Security.Cryptography.CryptoStreamMode.Write)
            cs.Write(inpBytes, 0, inpBytes.Length)
            cs.FlushFinalBlock()
            Dim ret As New System.Text.StringBuilder
            Dim b As Byte
            For Each b In ms.ToArray
                ret.AppendFormat("{0:X2}", b)
            Next
            Return ret.ToString
        End Function

        ''' <summary>
        ''' DES Decrypt string
        ''' </summary>
        ''' <param name="Text">String to decrypt</param>
        ''' <param name="Key">Decrypt key</param>
        ''' <param name="IV">Decrypt vector</param>
        ''' <returns>Decrypted string</returns>
        ''' <remarks></remarks>
        Public Shared Function DESDecrypt(ByVal Text As String, ByVal Key() As Byte, ByVal IV() As Byte) As String
            Dim des As New Security.Cryptography.DESCryptoServiceProvider
            Dim len As Integer = Text.Length / 2 - 1
            Dim inpBytes(len) As Byte
            Dim x, i As Integer
            For x = 0 To len
                i = Convert.ToInt32(Text.Substring(x * 2, 2), 16)
                inpBytes(x) = CType(i, Byte)
            Next
            des.Key = Key
            des.IV = IV
            Dim ms As New IO.MemoryStream
            Dim cs As New Security.Cryptography.CryptoStream(ms, des.CreateDecryptor, Security.Cryptography.CryptoStreamMode.Write)
            cs.Write(inpBytes, 0, inpBytes.Length)
            cs.FlushFinalBlock()
            Return System.Text.Encoding.UTF8.GetString(ms.ToArray)
        End Function

    End Class

End Namespace

